package cn.benma666.sjsj.znjh;

import cn.benma666.constants.UtilConst;
import cn.benma666.dict.Cllx;
import cn.benma666.dict.Ljpd;
import cn.benma666.dict.Xzms;
import cn.benma666.domain.SysLogJhjl;
import cn.benma666.domain.SysSjglFile;
import cn.benma666.domain.SysSjglZnjh;
import cn.benma666.domain.SysZnjhCtdy;
import cn.benma666.exception.MyException;
import cn.benma666.iframe.*;
import cn.benma666.myutils.DateUtil;
import cn.benma666.myutils.FileUtil;
import cn.benma666.json.JsonUtil;
import cn.benma666.myutils.StringUtil;
import cn.benma666.sjsj.web.LjqManager;
import cn.benma666.sjsj.web.UserManager;
import cn.benma666.sjzt.*;
import cn.benma666.sjzt.bdwj.BdwjFile;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.regex.Matcher;

import static cn.benma666.myutils.FileUtil.getFilePath;

public class MyZnjh extends BasicObject implements InterfaceZnjh {
    private static MyZnjh znjh;

    public static MyZnjh use(){
        if(znjh==null){
            znjh = new MyZnjh();
        }
        return znjh;
    }
    @Override
    public void znjh(SysSjglZnjh znjhConfig, InterfaceLog log, IFile ifile) {
        if(defaultCache.containsKey("clycwj"+znjhConfig.getId()+ifile.getName())){
            //处理异常的文件且在10分钟内跳过
            if((System.currentTimeMillis()-defaultCache.getLongValue("clycwj"
                    +znjhConfig.getId()+ifile.getName()))<600000){
                znjhConfig.getClzwj().remove(ifile.getName());
            }else {
                defaultCache.remove("clycwj"+znjhConfig.getId()+ifile.getName());
            }
            return;
        }
        log.debug("开始处理文件：{}",ifile.getAbsolutePath());

        SysLogJhjl wjcljl = new SysLogJhjl();
        try {
            wjcljl.setGzms(znjhConfig.getGzms());
            wjcljl.setCjsj(DateUtil.getGabDate());
            wjcljl.setWjzt("1");
            wjcljl.setBz("正常");
            wjcljl.setSfbl(Ljpd.FALSE.getCode());
            wjcljl.setLyml(ifile.getAbsolutePath());
            wjcljl.setLywjm(ifile.getName());
            wjcljl.setXgjh(znjhConfig.getId());
            wjcljl.setLysjzt(znjhConfig.getSrzt());
            wjcljl.setMbsjzt(znjhConfig.getSczt());
            wjcljl.setWjbh(znjhConfig.getAutoId().next());
            long size = ifile.length(DateUtil.scSjStrToLong(znjhConfig.getXwjdd()),DateUtil.scSjStrToLong(znjhConfig.getDwjdd()));
            wjcljl.setLywjdx(size);
            //生成指纹，当文件很大时很耗内存，暂时不获取指纹
//              wjcljl.setWjzw(FileUtil.getFileMD5(Utils.readByteArray(srzt.downloadFile(path))));
            if(znjhConfig.getBgfwjPattern()!=null&&znjhConfig.getBgfwjPattern()
                    .matcher(ifile.getName()).matches()){
                wjcljl.setBz("文件不规范！");
                wjcljl.setWjzt("3");
            }else if(!isBlank(znjhConfig.getDxxz())&&size> FileUtil.getSizeByStr(znjhConfig.getDxxz())){
                wjcljl.setBz("文件超出最大文件大小限制！");
                wjcljl.setWjzt("2");
            }else {
                JSONObject jhrw = DictManager.zdObjByDm("SYS_ZNJH_JHRW", znjhConfig.getId()
                        + ifile.getParent());
                if(jhrw!=null&&!isBlank(jhrw.getJSONObject("qqcs"))){
                    //收到文件发起请求
                    wjcljl.setGzms("7");
                    jxJswjm(ifile.getName(), wjcljl);
                    scqq(jhrw,wjcljl,ifile);
                }else {
                    BasicSjzt sczt = null;
                    if(jhrw==null||isBlank(jhrw.getString("mbzt"))){
                        if(!isBlank(znjhConfig.getSczt())){
                            //删除时没有输出载体
                            sczt = BasicSjzt.useSjzt(znjhConfig.getSczt());
                        }
                    }else {
                        sczt = BasicSjzt.useSjzt(jhrw.getString("mbzt"));
                    }
                    String scml;
                    if(jhrw==null||isBlank(jhrw.getString("mbml"))){
                        scml = ifile.getParent();
                    }else {
                        scml = jhrw.getString("mbml");
                    }
                    BasicFile scwj = new BdwjFile(scml,ifile.getName(),sczt);
                    scwj.setGzml(znjhConfig.getScml());
                    //获取输入流
                    switch (znjhConfig.getGzms()){
                        case "1":
                            //一般
                            ybcl(scwj,ifile);
                            break;
                        case "2":
                            //推送
                            tscl(wjcljl,scwj,ifile);
                            break;
                        case "3":
                            //接收
                            jscl(wjcljl,scwj,ifile);
                            break;
                        case "6":
                            scwj.setSjzt(null);
                            //删除文件，自带该功能
                            break;
                        default:
                            throw new MyException("不支持该工作模式："+znjhConfig.getGzms());
                    }
                    if(scwj.getSjzt()!=null){
                        //数据载体为空表示没有输出文件
                        wjcljl.setMbwjm(scwj.getName());
                        wjcljl.setMbml(scwj.getAbsolutePath());
                        if(!"4".equals(wjcljl.getGzms())){
                            //排除穿透调用场景，其他场景对目标文件进行大小校验,，这里主要针对ftp场景，避免文件没传完整
                            try {
                                wjcljl.setMbwjdx(scwj.length());
                                if(wjcljl.getMbwjdx()!=wjcljl.getLywjdx()){
                                    //损坏的目标文件进行删除
                                    scwj.delete();
                                    throw new MyException("目标文件损坏："+wjcljl.getLywjdx()+"->"+wjcljl.getMbwjdx());
                                }
                            }catch (SjztBzcwjdxExecption e){
                                //忽略该异常，有些载体不支持获取文件大小
                            }
                        }
                    }
                }
            }
            //备份文件
            if(StringUtil.isNotBlank(znjhConfig.getBfzt())){
                //设置了备份载体则进行备份
                BasicSjzt bfzt = BasicSjzt.useSjzt(znjhConfig.getBfzt());
                BdwjFile bfwj = new BdwjFile(getFilePath(znjhConfig.getBfml(),
//                        DateUtil.getDateTimeStr(DateUtil.DATE_FORMATTER10),
                        ifile.getParent()),System.currentTimeMillis()+"_"+ifile.getName(),bfzt);
                bfwj.save(ifile.getInputStream());
                wjcljl.setBfml(bfwj.getAbsolutePath());
            }
            if(znjhConfig.isScywj1()&&!ifile.delete()){
                //要删除监听文件，且删除失败
                throw new MyException("文件删除失败："+ifile.getAbsolutePath());
            }
        }catch (Throwable t){
            log.error("文件处理异常：{}",t.getMessage(),t);
            wjcljl.setWjzt("4");
            wjcljl.setBz(StringUtil.substrByByte("处理异常："+t.getMessage(),4000));
            //缓存处理异常文件，避免重复处理
            defaultCache.put("clycwj"+znjhConfig.getId()+ifile.getName(),System.currentTimeMillis());
        }finally {
            wjcljl.setGxsj(DateUtil.getGabDate());
            if(znjhConfig.isJlrz1()){
                sqlManager().insert(wjcljl);
            }else {
                log.info("不记录日志："+wjcljl);
            }
            //为避免文件重复处理，这里删除文件后延迟移除处理中文件
            try {
                Thread.sleep(200L);
            }catch (Exception e){
                log.debug("等待异常");
            }
            log.debug("完成处理文件：{}",ifile.getAbsolutePath());
            //处理完成，移除处理中记录
            znjhConfig.getClzwj().remove(ifile.getName());
        }
    }

    /**
     * 自动上传文件进行请求，系统收到文件后自动模拟上传（与人工上传一致），后调用相关处理接口
     * @param jhrw 交换任务
     * @param wjcljl 文件处理记录
     * @param ifile 待处理文件
     * @throws Exception 处理异常
     */
    private void scqq(JSONObject jhrw, SysLogJhjl wjcljl, IFile ifile) throws Exception {
        MyParams qqcs = new MyParams(jhrw.getJSONObject("qqcs"));
        //内部调用
        MyParams jcxx = LjqManager.jcxxByAuthCode("QTQX");
        SysSjglFile file = new SysSjglFile();
        file.setWjm(wjcljl.getMbwjm());
        file.setWjnr(Base64.getEncoder().encodeToString(ifile.getBytes()));
        jcxx.put(UtilConst.KEY_YOBJ,file);
        jcxx.sys().setCllx(Cllx.upload.name());
        Result r = LjqManager.data(jcxx);
        if(!r.isStatus()){
            //文件上传失败
            throw r.newMyException();
        }
        String sruser = StringUtil.fastNotNull(qqcs.znjh().getUser(), Conf.getUtilConfig().getZnjh().getUser(),UtilConst.SYS);
        jcxx = LjqManager.jcxxByParams(JsonUtil.clone(qqcs), UserManager.findUser(sruser));
        //设置上传的文件
        jcxx.sys().setFiles(Collections.singletonList(r.getData()));
        r = LjqManager.data(jcxx);
        //记录日志
        LjqManager.sendResult(jcxx ,r);
        if(!r.isStatus()){
            //请求失败
            throw r.newMyException();
        }
    }

    /**
     * 一般处理
     * @param scwj 输出文件
     * @param ifile 待处理文件
     * @throws Exception 处理异常
     */
    private void ybcl(BasicFile scwj, IFile ifile) throws Exception {
        scwj.save(ifile.getInputStream());
    }

    /**
     * 推送处理
     * @param wjcljl 文件处理记录
     * @param scwj 输出文件
     * @param ifile 待处理文件
     * @throws Exception 处理异常
     */
    private void tscl(SysLogJhjl wjcljl, BasicFile scwj, IFile ifile) throws Exception {
        scwj.setName(getTswjm(ifile.getName(), wjcljl.getWjbh(), ifile.length()));
        //TODO 增加大文件分割传输机制
        //TODO 增加加密支持
        scwj.save(ifile.getInputStream());
    }

    /**
     * 接收处理
     * @param wjcljl 文件处理记录
     * @param scwj 输出文件
     * @param ifile 待处理文件
     * @throws Exception 处理异常
     */
    private void jscl(SysLogJhjl wjcljl, BasicFile scwj, IFile ifile) throws Exception {
        jxJswjm(ifile.getName(), wjcljl);
        scwj.setName(wjcljl.getMbwjm());
        //TODO 增加解密支持
        //TODO 增加大文件分割传输后合并机制
        if(ifile.getParent().startsWith("/ctdy")){
            ctdy(wjcljl,scwj,ifile);
        }else if(ifile.getParent().startsWith("/ctjg")){
            ctjg(wjcljl,scwj,ifile);
        }else {
            scwj.save(ifile.getInputStream());
        }
    }

    /**
     * 穿透调用
     * @param wjcljl 文件处理记录
     * @param scwj 输出文件
     * @param ifile 待处理文件
     * @throws Exception 处理异常
     */
    private void ctdy(SysLogJhjl wjcljl, BasicFile scwj, IFile ifile) throws Exception {
        wjcljl.setGzms("4");
        //判断目录为穿透调用则直接进行接口调用
        MyParams myParams = MyParams.parseObject(new String(ifile.getBytes(), StandardCharsets.UTF_8),
                Feature.OrderedField);
        //根据来源交换节点查找对应的穿透调用进行结果推送
        SysZnjhCtdy ctdy = getCtdyByMbjd(myParams.znjh().getMbjd());
        //理论上这里的user是一定会传的
        String scuser = StringUtil.fastNotNull(myParams.znjh().getUser(),
                Conf.getUtilConfig().getZnjh().getUser(),UtilConst.SYS);
        Result result;
        MyParams jcxx = null;
        try {
            //本地方法场景
            jcxx = LjqManager.jcxxByParams(JsonUtil.clone(myParams),UserManager.findUser(scuser),false);
            //设置下载模式，处理文件传输场景
            jcxx.sys().setXzms(Xzms.BASE64OBJ.getCode());
            result = LjqManager.data(jcxx);
        }catch (MyException t){
            result = t.getResult();
        }catch (Throwable t){
            log.debug("穿透调用失败",t);
            result = failed("穿透后调用异常："+t.getMessage(),t);
        }
        //记录日志
        if(jcxx!=null){
            try {
                LjqManager.sendResult(jcxx ,result);
            }catch (Throwable t){
                log.warn("写入访问日志失败",t);
                result.addMsg("写入访问日志失败："+t.getMessage());
            }
        }
        if(!valByDef(myParams.getBoolean("$.sys.ctdyjg"),true)){
            scwj.setSjzt(null);
            log.info("该穿透调用不需要返回结果：{}",result);
            return;
        }
        if (isBlank(ctdy.getDm())) {
            scwj.setSjzt(null);
            log.info("该穿透调用没有配置返回结果穿透调用：{}",result);
            return;
        }
        byte[] bArr = result.toString().getBytes(StandardCharsets.UTF_8);
        scwj.setSjzt(BasicSjzt.useSjzt(ctdy.getTsjhObj().getSrzt()));
        scwj.setParent("/ctjg");
        scwj.setGzml(getFilePath(ctdy.getTsjhObj().getSrml(),ctdy.getTsml()));
        scwj.setName(IFile.RESULT+ wjcljl.getMbwjm());
        wjcljl.setMbsjzt(scwj.getSjzt().getName());
        wjcljl.setMbwjdx(bArr.length);
        scwj.save(new ByteArrayInputStream(bArr));
    }

    public static SysZnjhCtdy getCtdyByMbjd(String mbjd) {
        String ctdyKey = "ctdymbjd" + mbjd;
        SysZnjhCtdy ctdy = defaultCache.getObject(ctdyKey, SysZnjhCtdy.class);
        if(ctdy==null){
            ctdy = Db.use().lambdaQuery(SysZnjhCtdy.class)
                    .andEq(SysZnjhCtdy::getTsjd,Conf.getAppdm())
                    .andLike(SysZnjhCtdy::getMbjd, "%"+mbjd+"%").asc(SysZnjhCtdy::getPx)
                    .singleSimple();
            //缓存一下，避免每次查询
            if(ctdy==null){
                ctdy = new SysZnjhCtdy();
            }
            defaultCache.put(ctdyKey,ctdy);
        }
        slog.trace("根据目标节点获取穿透调用{}-》{}",mbjd,ctdy);
        return ctdy;
    }

    /**
     * 穿透结果
     * @param wjcljl 文件处理记录
     * @param scwj 输出文件
     * @param ifile 待处理文件
     * @throws Exception 处理异常
     */
    private void ctjg(SysLogJhjl wjcljl, BasicFile scwj, IFile ifile) throws Exception {
        scwj.setSjzt(null);
        wjcljl.setGzms("5");
        //判断目录为穿透调用结果则唤醒调用等待
        Result result = Result.parse(new String(ifile.getBytes(),StandardCharsets.UTF_8));
        //查看是否有请求在等待调用结果，有则释放等待，没有则打印日志结束
        Lock lock = BasicFile.ctdyMap.get(wjcljl.getMbwjm());
        if(lock==null){
            log.info("没有等待的穿透调用：{}",result);
            return;
        }
        Condition cond = BasicFile.ctdyCondMap.get(wjcljl.getMbwjm());
        //设置调用结果
        BasicFile.ctjgMap.put(wjcljl.getMbwjm(),result);
        //唤醒等待
        lock.lock();
        cond.signal();
        lock.unlock();
    }

    /**
     * @return 结果文件名
     */
    private static String getTswjm(String fileName, long id, long length) {
        return DateUtil.getGabDate() + "_"+ id+"_" + length + "_" + fileName;
    }
    /**
     * 解析接收文件名
     * @param file 待解析的文件名
     * @param wjcljl 文件处理记录
     */
    private static void jxJswjm(String file, SysLogJhjl wjcljl) {
        Matcher matcher = BasicFile.jswjPattern.matcher(new File(file).getName());
        List<String> sa = new ArrayList<>();
        if (matcher.find()) {
            for (int i = 1; i <= matcher.groupCount(); i++) {
                sa.add(matcher.group(i));
            }
        } else {
            throw new MyException(file + "不是正确的接收文件");
        }
        wjcljl.setWjbh(Long.parseLong(sa.get(1)));
        wjcljl.setMbwjm(sa.get(3));
//        //推送文件大小 从文件名中截取
//        wjcljl.setLywjdx(Long.parseLong(sa.get(2)));
//        //通过文件名切出 推送时间
//        wjcljl.setCjsj(sa.get(0));
    }
}
